/*******************************************************************************
    Project: JobShop Problem using Genetic Algorithm: Serial Approach
    File: main.cpp
    Author: Ameya Deswandikar
    Email: ameya.des1@gmail.com
    Revision: 0.1
    Date & Time: 11/08/2013

    Description:
    Genetic Algorithm to solve Job Shop Scheduling, a serial implementation
*******************************************************************************/

/*
** ----------------------------------------------------------------------------
** S Y S T E M   I N C L U D E S
** ----------------------------------------------------------------------------
*/
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstring>


/*
** ----------------------------------------------------------------------------
** C O M P O N E N T   I N C L U D E S
** ----------------------------------------------------------------------------
*/
#include "types.h"
#include "crossover.h"
#include "FileOps.h"
#include "EvaluateIndividual.h"
#include "UniverseGenerator.h"


/*
** ----------------------------------------------------------------------------
** C O N F I G U R A T I O N   P A R A M E T E R S
** ----------------------------------------------------------------------------
*/

// If defined, debug prints will be visible on stdout
#define __debug__

// The maximum number of allowed generations. GA may converge earlier
#define NUM_GENERATIONS (10)

// Technology matrix
unsigned int **T;

// Processing Time matrix
unsigned int **P;

// Number of Jobs
unsigned int NumJobs;

// Number of Machines
unsigned int NumMachines;

// Used to compute the total wall clock execution time.
struct timespec StartTime;
struct timespec EndTime;

extern unsigned int rand_seeds[10];


/*============================================================================
**
** Function Name:       main
**
** Description:         This function implements the overall genetic
** 						algorithm, processes command line arguments
**
** Invocation:          System on application startup
**
** Input Parameters:    int argc 	: The argument count
** 						char** argv	: The argument list
**
** Return Values:       int (0 if problems, 1 otherwise)
**
**==========================================================================*/

int main(int argc,char** argv)
{
    // Command line argument validation
    if(argc != 9)
    {
        std::cerr << "JobShop problem using genetic algorithms" << std::endl;
    	std::cerr << "Usage: " << argv[0] << "-b <Benchmark File> -p <Initial Population> -n <Number of generations> -c <Crossover part 0-1>" << std::endl;
        return 0;
    }
    else
    if(0 != strcmp(argv[1],"-b") || 0 != strcmp(argv[3],"-p") || 0 != strcmp(argv[5],"-n") || 0 != strcmp(argv[7],"-c"))
    {
        std::cerr << "JobShop problem using genetic algorithms" << std::endl;
    	std::cerr << "Usage: " << argv[0] << "-b <Benchmark File> -p <Initial Population> -n <Number of generations> -c <Crossover part 0-1>" << std::endl;
        return 0;
    }

    bool done = false;

    // Seed the randomizer
    srand(time(NULL));

    for(unsigned int i = 0; i < RS_MAX_RAND_SEEDS; i++)
    {
    	rand_seeds[i] = rand();
    }

    // Create Universe
    unsigned long long int population = atoi(argv[4]);
    if(0 == population)
    {
    	std::cerr << "Population cannot be zero!" << std::endl;
    	return 0;
    }

    unsigned long int generations = atoi(argv[6]);
    if(0 == generations)
	{
		std::cerr << "Generations cannot be zero!" << std::endl;
		return 0;
	}

    float crossover = atof(argv[8]);
	if(crossover <= 0 || crossover >= 1)
	{
		std::cerr << "0 < Crossover < 1 !" << std::endl;
		return 0;
	}

    // Start Wall Clock
    clock_gettime(CLOCK_REALTIME, &StartTime);

    //1. Reading Benchmarks
    FILE* fp = fopen(argv[2],"r+");

    // File validation
    if(!fp)
    {
    	std::cerr << "File " << argv[2] << " not found!" << std::endl;
    	return 0;
    }

    // Parse the benchmark file
    ReadBenchMark(fp, &NumJobs, &NumMachines);

    //2. Create the universe
    Individual* universe 		 = new Individual[population];
    Individual* SortedPopulation = new Individual[population];
    Individual* NewPopulation    = new Individual[population];

    // Fittest individual of current generation
    Individual indFittestCurrent;

    // Fittest individual of current generation
    Individual indFittestPrev;

    //Fill the universe with individuals
    CreateUniverse(population, universe,NumJobs,NumMachines);

    float curr_gen_avg_fitness = 0;
    float avg_prev_gen_fitness;

    //Evaluate the fitness of each individual
    for(unsigned int i = 0; i < population; i++)
    	EvaluateIndividual(universe[i],NumJobs, NumMachines,&T,&P,done);

    //Sort the population according to their fitness and fill it in SortedPopulation
    avg_prev_gen_fitness = SortPopulation(universe,population,SortedPopulation);

    unsigned long int gen = 0;
    while(1)
    {
		indFittestPrev = SortedPopulation[0];

    	// Create population of offsprings
    	CreateNewPopulation(SortedPopulation,population,NewPopulation,NumJobs,NumMachines, crossover);

        for(unsigned int i = 0; i < population; i++)
            EvaluateIndividual(NewPopulation[i],NumJobs, NumMachines,&T,&P,false);

        // Sort the population of offsprings
        curr_gen_avg_fitness = SortPopulation(NewPopulation,population,SortedPopulation);

        indFittestCurrent = SortedPopulation[0];

#if 0
        if(indFittestCurrent.Fitness >= indFittestPrev.Fitness)
        {
            done = true;
            EvaluateIndividual(indFittestPrev,NumJobs, NumMachines,&T,&P,true);
            break;
        }
#endif

#ifdef __debug__
        cout << "Gen " << gen << " Best Fitness = " << indFittestCurrent.Fitness << " Avg Fitness = " << curr_gen_avg_fitness << endl;
#endif
        // Genetic Algorithm terminating condition
        if(curr_gen_avg_fitness > avg_prev_gen_fitness)
        {
        	break;
        }
        avg_prev_gen_fitness = curr_gen_avg_fitness;

        indFittestPrev = indFittestCurrent;
        gen++;

    }

    // Stop Wall Clock
    clock_gettime(CLOCK_REALTIME, &EndTime);

	//Reporting Results
	unsigned long long int runtime = 1000000000 * (EndTime.tv_sec - StartTime.tv_sec) + EndTime.tv_nsec - StartTime.tv_nsec;
  	printf("(%d.%09lld sec)\n", runtime / 1000000000, runtime % 1000000000);


    //Cleanup
    unsigned int i;

    // Delete allocated memory
    if(universe)
        delete[] universe;

    if(T)
        for(i = 0; i < NumMachines; i++)
            delete[] T[i];

    if(P)
        for(i = 0; i < NumMachines; i++)
            delete[] P[i];
    
    if(SortedPopulation)
    	delete[] SortedPopulation;

    if(NewPopulation)
    	delete[] NewPopulation;

    return 0;

}
